// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.

package nachos.machine;

import nachos.security.*;

/**
 * A hardware timer generates a CPU timer interrupt approximately every 500
 * clock ticks. This means that it can be used for implementing time-slicing,
 * or for having a thread go to sleep for a specific period of time.
 * <p>
 * The <tt>Timer</tt> class emulates a hardware timer by scheduling a timer
 * interrupt to occur every time approximately 500 clock ticks pass. There is
 * a small degree of randomness here, so interrupts do not occur exactly every
 * 500 ticks.
 */
public final class Timer
{
    /**
     * Allocate a new timer.
     *
     * @param    privilege encapsulates privileged access to the Nachos
     * machine.
     */
    public Timer(Privilege privilege)
    {
        System.out.print(" timer");

        this.privilege = privilege;

        timerInterrupt = new Runnable()
        {
            public void run()
            {
                timerInterrupt();
            }
        };

        autoGraderInterrupt = new Runnable()
        {
            public void run()
            {
                Machine.autoGrader().timerInterrupt(Timer.this.privilege,
                        lastTimerInterrupt);
            }
        };

        scheduleInterrupt();
    }

    /**
     * Set the callback to use as a timer interrupt handler. The timer
     * interrupt handler will be called approximately every 500 clock ticks.
     *
     * @param    handler        the timer interrupt handler.
     */
    public void setInterruptHandler(Runnable handler)
    {
        this.handler = handler;
    }

    /**
     * Get the current time.
     *
     * @return the number of clock ticks since Nachos started.
     */
    public long getTime()
    {
        return privilege.stats.totalTicks;
    }

    private void timerInterrupt()
    {
        scheduleInterrupt();
        scheduleAutoGraderInterrupt();

        lastTimerInterrupt = getTime();

        if (handler != null)
            handler.run();
    }

    private void scheduleInterrupt()
    {
        int delay = Stats.TimerTicks;
        delay += Lib.random(delay / 10) - (delay / 20);

        privilege.interrupt.schedule(delay, "timer", timerInterrupt);
    }

    private void scheduleAutoGraderInterrupt()
    {
        privilege.interrupt.schedule(1, "timerAG", autoGraderInterrupt);
    }

    private long lastTimerInterrupt;
    private Runnable timerInterrupt;
    private Runnable autoGraderInterrupt;

    private Privilege privilege;
    private Runnable handler = null;
}
